package network

import (
	"gitee.com/amoyx/cloud-provider/pkg/cloud/tencent/models"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	tvpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
)

type NATGreeter interface {
	NAT(region string) NATInterface
}

type NATInterface interface {
	Create(args models.NATRequest) (result models.CreateResponseParams, err error)
	CreateExistIP(args models.NATRequest) (result models.CreateResponseParams, err error)
	Get(id string) (result models.NATResponse, err error)
	Update(id string, args models.NATRequest) (result models.CreateResponseParams, err error)
	Delete(id string) (err error)
	AddIpPortTranslationRules(args []models.IpPortTranslationNATRule) (result models.CreateResponseParams, err error)
	SNATForSubnet(id string, args []models.GatewaySourceIpTranslationNatRule) (result models.CreateResponseParams, err error)
	SNATForCloudHost(id string, args []models.GatewaySourceIpTranslationNatRule) (result models.CreateResponseParams, err error)
	SNATForUserDefinition(id string, args []models.GatewaySourceIpTranslationNatRule) (result models.CreateResponseParams, err error)
	UpdateNatGatewayIpAddress(id string, args models.NATRequest) (result models.CreateResponseParams, err error)
	ListByName(name string) (list []models.NATResponse, err error)
	ListByVpc(vpcId string) (list []models.NATResponse, err error)
	List() (list []models.NATResponse, err error)
}

type nat struct {
	network *Network
	region  string
	client  *tvpc.Client
}

func newNAT(network *Network, region string) NATInterface {
	v := &nat{
		network: network,
		region:  region,
	}
	cpf := profile.NewClientProfile()
	cpf.HttpProfile.Endpoint = string(VpcEndPoint)
	client, _ := tvpc.NewClient(v.network.Credential(), v.region, cpf)
	v.client = client
	return v
}

func (n *nat) Get(id string) (result models.NATResponse, err error) {
	list, err := n.query(map[string]string{"nat-gateway-name": id})
	if len(list) > 0 {
		result = list[0]
	}
	return
}

// Update 更新NAT基本信息，只能更新 实例名称和带宽大小
func (n *nat) Update(id string, args models.NATRequest) (result models.CreateResponseParams, err error) {
	request := tvpc.NewModifyNatGatewayAttributeRequest()
	request.NatGatewayName = common.StringPtr(id)
	request.InternetMaxBandwidthOut = common.Uint64Ptr(args.InternetMaxBandwidthOut)
	response, err := n.client.ModifyNatGatewayAttribute(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	return
}

func (n *nat) Create(args models.NATRequest) (result models.CreateResponseParams, err error) {
	return n.newAdd("new", args)
}

func (n *nat) CreateExistIP(args models.NATRequest) (result models.CreateResponseParams, err error) {
	return n.newAdd("exist", args)
}

func (n *nat) newAdd(natType string, args models.NATRequest) (result models.CreateResponseParams, err error) {
	request := tvpc.NewCreateNatGatewayRequest()
	request.NatGatewayName = common.StringPtr(args.NatGatewayName)
	request.VpcId = common.StringPtr(args.VpcId)
	request.InternetMaxBandwidthOut = common.Uint64Ptr(args.InternetMaxBandwidthOut)
	request.MaxConcurrentConnection = common.Uint64Ptr(args.MaxConcurrentConnection)
	if natType == "exist" {
		request.PublicIpAddresses = common.StringPtrs(args.PublicIpAddresses)
	} else {
		request.AddressCount = common.Uint64Ptr(args.AddressCount)
		request.PublicIpAddressesBandwidthOut = common.Uint64Ptr(args.PublicIpAddressesBandwidthOut)
	}
	response, err := n.client.CreateNatGateway(request)
	result.RequestId = *response.Response.RequestId
	var ids []string
	for _, row := range response.Response.NatGatewaySet {
		ids = append(ids, *row.NatGatewayId)
	}
	return
}

func (n *nat) Delete(id string) (err error) {
	request := tvpc.NewDeleteNatGatewayRequest()
	request.NatGatewayId = common.StringPtr(id)
	_, err = n.client.DeleteNatGateway(request)
	return
}

// UpdateNatGatewayIpAddress 更新NAT绑定IP地址
func (n *nat) UpdateNatGatewayIpAddress(id string, args models.NATRequest) (result models.CreateResponseParams, err error) {
	request := tvpc.NewAssociateNatGatewayAddressRequest()
	request.NatGatewayId = common.StringPtr(id)
	request.AddressCount = common.Uint64Ptr(args.AddressCount)
	request.PublicIpAddresses = common.StringPtrs(args.PublicIpAddresses)
	request.PublicIpAddressesBandwidthOut = common.Uint64Ptr(args.PublicIpAddressesBandwidthOut)
	response, err := n.client.AssociateNatGatewayAddress(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	return
}

// AddIpPortTranslationRules  增加IP端口转发规则
func (n *nat) AddIpPortTranslationRules(args []models.IpPortTranslationNATRule) (result models.CreateResponseParams, err error) {
	request := tvpc.NewCreateNatGatewayDestinationIpPortTranslationNatRuleRequest()
	var rules []*tvpc.DestinationIpPortTranslationNatRule
	for _, r := range args {
		rules = append(rules, &tvpc.DestinationIpPortTranslationNatRule{
			IpProtocol:       common.StringPtr(r.IpProtocol),
			PublicIpAddress:  common.StringPtr(r.PublicIpAddress),
			PublicPort:       common.Uint64Ptr(r.PublicPort),
			PrivateIpAddress: common.StringPtr(r.PrivateIpAddress),
			PrivatePort:      common.Uint64Ptr(r.PrivatePort),
			Description:      common.StringPtr(r.Description),
		})
	}
	request.DestinationIpPortTranslationNatRules = rules
	response, err := n.client.CreateNatGatewayDestinationIpPortTranslationNatRule(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	return
}

// SNATForSubnet 添加子网类型 SNAT转发规则
func (n *nat) SNATForSubnet(id string, args []models.GatewaySourceIpTranslationNatRule) (result models.CreateResponseParams, err error) {
	return n.snat(id, "SUBNET", args)
}

// SNATForCloudHost 添加云服务类型 SNAT转发规则
func (n *nat) SNATForCloudHost(id string, args []models.GatewaySourceIpTranslationNatRule) (result models.CreateResponseParams, err error) {
	return n.snat(id, "NETWORKINTERFACE", args)
}

// SNATForUserDefinition 添加自定义网段类型 SNAT转发规则
func (n *nat) SNATForUserDefinition(id string, args []models.GatewaySourceIpTranslationNatRule) (result models.CreateResponseParams, err error) {
	return n.snat(id, "USERDEFINED", args)
}

func (n *nat) snat(id string, snatType string, args []models.GatewaySourceIpTranslationNatRule) (result models.CreateResponseParams, err error) {
	request := tvpc.NewCreateNatGatewaySourceIpTranslationNatRuleRequest()
	request.NatGatewayId = common.StringPtr(id)
	var rules []*tvpc.SourceIpTranslationNatRule
	for _, r := range args {
		rule := &tvpc.SourceIpTranslationNatRule{}
		switch {
		case snatType == "SUBNET":
			rule.VpcId = common.StringPtr(r.VpcId)
			rule.ResourceId = common.StringPtr(r.ResourceId)
		case snatType == "NETWORKINTERFACE":
			rule.VpcId = common.StringPtr(r.VpcId)
			rule.ResourceId = common.StringPtr(r.ResourceId)
			rule.PrivateIpAddress = common.StringPtr(r.PrivateIpAddress)
		case snatType == "USERDEFINED":
			rule.PrivateIpAddress = common.StringPtr(r.PrivateIpAddress)
		}
		rule.PublicIpAddresses = common.StringPtrs(r.PublicIpAddresses)
		rule.Description = common.StringPtr(r.Description)
		rules = append(rules, rule)
	}
	response, err := n.client.CreateNatGatewaySourceIpTranslationNatRule(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	return
}

// QueryIpPortTranslationRules 查询IP端口转发规则
func (n *nat) QueryIpPortTranslationRules(id string) (list []*tvpc.NatGatewayDestinationIpPortTranslationNatRule, err error) {
	request := tvpc.NewDescribeNatGatewayDestinationIpPortTranslationNatRulesRequest()
	request.NatGatewayIds = common.StringPtrs([]string{id})
	request.Offset = common.Uint64Ptr(0)
	request.Limit = common.Uint64Ptr(100)
	response, err := n.client.DescribeNatGatewayDestinationIpPortTranslationNatRules(request)
	if err != nil {
		return
	}
	list = response.Response.NatGatewayDestinationIpPortTranslationNatRuleSet
	return
}

// QuerySourceIpTranslationRules 查询SNAT转发规则
func (n *nat) QuerySourceIpTranslationRules(id string) (list []*tvpc.SourceIpTranslationNatRule, err error) {
	request := tvpc.NewDescribeNatGatewaySourceIpTranslationNatRulesRequest()
	request.NatGatewayId = common.StringPtr(id)
	request.Offset = common.Int64Ptr(0)
	request.Limit = common.Int64Ptr(100)
	response, err := n.client.DescribeNatGatewaySourceIpTranslationNatRules(request)
	if err != nil {
		return
	}
	list = response.Response.SourceIpTranslationNatRuleSet
	return
}

func (n *nat) ListByName(name string) (list []models.NATResponse, err error) {
	return n.query(map[string]string{"nat-gateway-name": name})
}

func (n *nat) ListByVpc(vpcId string) (list []models.NATResponse, err error) {
	return n.query(map[string]string{"vpc-id": vpcId})
}

func (n *nat) List() (list []models.NATResponse, err error) {
	return n.query(map[string]string{})
}

func (n *nat) query(args map[string]string) (list []models.NATResponse, err error) {
	request := tvpc.NewDescribeNatGatewaysRequest()
	var filters []*tvpc.Filter
	for key, val := range args {
		filters = append(filters, &tvpc.Filter{
			Name:   common.StringPtr(key),
			Values: common.StringPtrs([]string{val}),
		})
	}
	request.Offset = common.Uint64Ptr(0)
	request.Limit = common.Uint64Ptr(100)
	response, err := n.client.DescribeNatGateways(request)
	for _, row := range response.Response.NatGatewaySet {
		var sglist []string
		for _, sg := range row.SecurityGroupSet {
			if sg != nil {
				sglist = append(sglist, *sg)
			}
		}
		var ipaddrs []models.NatGatewayAddress
		for _, ip := range row.PublicIpAddressSet {
			ipaddrs = append(ipaddrs, models.NatGatewayAddress{
				AddressId:       *ip.AddressId,
				PublicIpAddress: *ip.PublicIpAddress,
				IsBlocked:       *ip.IsBlocked,
				BlockType:       *ip.BlockType,
			})
		}
		list = append(list, models.NATResponse{
			NatGatewayId:            *row.NatGatewayId,
			NatGatewayName:          *row.NatGatewayName,
			CreatedTime:             *row.CreatedTime,
			State:                   *row.State,
			InternetMaxBandwidthOut: *row.InternetMaxBandwidthOut,
			MaxConcurrentConnection: *row.MaxConcurrentConnection,
			PublicIpAddressSet:      ipaddrs,
			NetworkState:            *row.NetworkState,
			VpcId:                   *row.VpcId,
			Zone:                    *row.Zone,
			SecurityGroupSet:        sglist,
		})
	}
	return
}
